package com.sg.business.performence.model;

import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.bson.types.ObjectId;
import org.eclipse.swt.graphics.Color;

import com.mobnut.commons.util.Utils;
import com.mobnut.db.DBActivator;
import com.mobnut.db.model.IContext;
import com.mobnut.db.model.PrimaryObject;
import com.mobnut.db.utils.DBUtil;
import com.mongodb.AggregationOutput;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.sg.business.model.ILifecycle;
import com.sg.business.model.IModelConstants;
import com.sg.business.model.Project;
import com.sg.business.model.User;
import com.sg.business.model.Work;
import com.sg.business.model.WorkTimeProgram;

public class ProjectWorksItem extends PrimaryObject {
	private PrimaryObject data;
	private Double totalWorks;
	private Map<String, StatisticsStepWorkTime> statisticsStepWorkTime;

	class StatisticsStepWorkTime {
		private Double planWorks;
		private Double actualWorks;
		private boolean finishType;
		private int actualFinishYear;
		private int actualFinishMonth;
		private int planFinishYear;
		private int planFinishMonth;

		StatisticsStepWorkTime(DBObject dbo, ObjectId project_id) {
			this.planWorks = Double.valueOf(dbo.get(Work.F_PLAN_WORKS)
					.toString());
			this.actualWorks = 0d;
			this.finishType = Boolean.TRUE;

			Calendar cal = Calendar.getInstance();
			DBCollection workCol = getWorkCollection();
			String statisticsStep = (String) dbo.get(F__ID);

			BasicDBObject match = getMatch(project_id);
			match.put(Work.F_STATISTICS_STEP, statisticsStep);

			DBObject planFinishObject = workCol.findOne(match,
					new BasicDBObject().append(Work.F_PLAN_FINISH, 1),
					new BasicDBObject().append(Work.F_PLAN_FINISH, -1));
			if (planFinishObject != null) {
				Date planFinish = (Date) planFinishObject
						.get(Work.F_PLAN_FINISH);
				if (planFinish != null) {
					cal.setTime(planFinish);
					this.planFinishYear = cal.get(Calendar.YEAR);
					this.planFinishMonth = cal.get(Calendar.MONTH);
				}
			}
			match.put(Work.F_LIFECYCLE, ILifecycle.STATUS_FINIHED_VALUE);

			BasicDBObject group = new BasicDBObject().append(
					"$group",
					new BasicDBObject().append(F__ID,
							"$" + Work.F_STATISTICS_STEP).append(
							Work.F_ACTUAL_WORKS,
							new BasicDBObject().append("$sum", "$"
									+ Work.F_ACTUAL_WORKS)));

			AggregationOutput aggregate = DBUtil.aggregate(workCol,
					new BasicDBObject().append("$match", match), group);
			Iterable<DBObject> results = aggregate.results();
			for (DBObject result : results) {
				Object actualworks = result.get(Work.F_ACTUAL_WORKS);
				if (actualworks != null) {
					this.actualWorks += Double.valueOf(actualworks.toString());
				}
			}

			DBObject actualFinishObject = workCol.findOne(match,
					new BasicDBObject().append(Work.F_ACTUAL_FINISH, 1),
					new BasicDBObject().append(Work.F_ACTUAL_FINISH, -1));
			if (actualFinishObject != null) {
				Date actualFinish = (Date) actualFinishObject
						.get(Work.F_ACTUAL_FINISH);
				if (actualFinish != null) {
					cal.setTime(actualFinish);
					this.actualFinishYear = cal.get(Calendar.YEAR);
					this.actualFinishMonth = cal.get(Calendar.MONTH);
				}
			}

			match.put(
					Work.F_LIFECYCLE,
					new BasicDBObject().append("$nin", new String[] {
							ILifecycle.STATUS_CANCELED_VALUE,
							ILifecycle.STATUS_FINIHED_VALUE }));
			long count = workCol.count(match);
			if (count > 0) {
				this.finishType = Boolean.FALSE;
			}

		}

		public int getActualFinishYear() {
			return actualFinishYear;
		}

		public int getActualFinishMonth() {
			return actualFinishMonth;
		}

		public int getPlanFinishYear() {
			return planFinishYear;
		}

		public int getPlanFinishMonth() {
			return planFinishMonth;
		}

		public Double getPlanWorks() {
			return planWorks;
		}

		public Double getActualWorks() {
			return actualWorks;
		}

		public boolean isFinishType() {
			return finishType;
		}
	}

	public ProjectWorksItem(PrimaryObject data, int year, int month) {
		this.data = data;
		ObjectId _id = data.get_id();
		DBCollection workCol = getWorkCollection();
		this.statisticsStepWorkTime = new HashMap<String, StatisticsStepWorkTime>();
		this.totalWorks = 0d;

		BasicDBObject match = getMatch(_id);
		BasicDBObject group = new BasicDBObject().append(
				"$group",
				new BasicDBObject().append(F__ID, "$" + Work.F_STATISTICS_STEP)
						.append(Work.F_PLAN_WORKS,
								new BasicDBObject().append("$sum", "$"
										+ Work.F_PLAN_WORKS)));
		AggregationOutput aggregate = DBUtil.aggregate(workCol,
				new BasicDBObject().append("$match", match), group);
		Iterable<DBObject> results = aggregate.results();
		for (DBObject dbo : results) {
			StatisticsStepWorkTime workTime = new StatisticsStepWorkTime(dbo,
					_id);
			this.statisticsStepWorkTime.put((String) dbo.get(F__ID), workTime);
			Object planWorks = dbo.get(Work.F_PLAN_WORKS);
			if (planWorks != null) {
				this.totalWorks += Double.valueOf(planWorks.toString());
			}
		}

		super.set_data(data.get_data());
	}

	private BasicDBObject getMatch(ObjectId _id) {
		return new BasicDBObject()
				.append(Work.F_PROJECT_ID, _id)
				.append("$and",
						new BasicDBObject[] {
								new BasicDBObject()
										.append(Work.F_STATISTICS_STEP,
												new BasicDBObject().append(
														"$ne", null)),
								new BasicDBObject().append(
										Work.F_STATISTICS_STEP,
										new BasicDBObject().append("$ne", "")) })
				.append(Work.F_MEASUREMENT,
						new BasicDBObject().append("$ne",
								Work.MEASUREMENT_TYPE_NO_ID));
	}

	private DBCollection getWorkCollection() {
		return DBActivator.getCollection(IModelConstants.DB,
				IModelConstants.C_WORK);
	}

	private PrimaryObject getPrimaryObject() {
		return data;
	}

	public String getProjectNumber() {
		Project project = (Project) getPrimaryObject();
		return project.getProjectNumber();
	}

	public String getProjectDesc() {
		Project project = (Project) getPrimaryObject();
		return project.getDesc();
	}

	public String getProjectCharger() {
		Project project = (Project) getPrimaryObject();
		User charger = project.getCharger();
		if (charger != null) {
			return charger.getLabel();
		}
		return "";
	}

	public String getWorkTimeProgram() {
		Project project = (Project) getPrimaryObject();
		WorkTimeProgram workTimeProgram = project.getWorkTimeProgram();
		if (workTimeProgram != null) {
			return workTimeProgram.getDesc();
		}
		return null;
	}

	public String getProjectType() {
		Project project = (Project) getPrimaryObject();
		List<String> projectTypeOptions = project.getProjectTypeOptions();
		if (projectTypeOptions != null) {
			String projectType = null;
			for (String projectTypeOption : projectTypeOptions) {
				if (projectType == null) {
					projectType = projectTypeOption;
				} else {
					projectType += "," + projectTypeOption;
				}
			}
			return projectType;
		}
		return null;
	}

	public String getTotalWorkTime() {
		DecimalFormat df = new DecimalFormat(Utils.NF_NUMBER_P2);
		return df.format(totalWorks);
	}

	public String getStatisticsStepWorkTime(String statisticsStep, int year,
			int month) {
		StatisticsStepWorkTime workTime = statisticsStepWorkTime
				.get(statisticsStep);
		if (workTime != null) {
			DecimalFormat df = new DecimalFormat(Utils.NF_NUMBER_P2);
			if (workTime.isFinishType()
					&& year == workTime.getActualFinishYear()
					&& month == workTime.getActualFinishMonth()) {
				return df.format(workTime.getActualWorks());
			}

			return df.format(workTime.getPlanWorks());
		}
		return null;
	}

	public Color getStatisticsStepBackground(String statisticeStep, int year,
			int month, Color[] bgColors) {
		StatisticsStepWorkTime workTime = statisticsStepWorkTime
				.get(statisticeStep);
		if (workTime != null) {
			int actualFinishYear = workTime.getActualFinishYear();
			int actualFinishMonth = workTime.getActualFinishMonth();
			if (workTime.isFinishType()) {
				if (year == actualFinishYear) {
					if (month == actualFinishMonth) {
						return bgColors[1];
					} else if (month > actualFinishMonth) {
						return bgColors[0];
					}
				} else if (year > actualFinishYear) {
					return bgColors[0];
				}
			}
		}
		return bgColors[2];
	}

	public String getProjectMonthWorkTime(int year, int month) {
		DecimalFormat df = new DecimalFormat(Utils.NF_NUMBER_P2);
		Double monthWorkTime = 0d;
		Set<String> keySet = statisticsStepWorkTime.keySet();
		for (String key : keySet) {
			StatisticsStepWorkTime workTime = statisticsStepWorkTime.get(key);
			if (workTime != null) {
				if (workTime.isFinishType()
						&& year == workTime.getActualFinishYear()
						&& month == workTime.getActualFinishMonth()) {
					monthWorkTime += workTime.getActualWorks();
				}
			}
		}
		return df.format(monthWorkTime);
	}

	@Override
	public void doInsert(IContext context) throws Exception {
	}

	@Override
	public void doRemove(IContext context) throws Exception {
	}

	@Override
	public boolean doSave(IContext context) throws Exception {
		return true;
	}

	@Override
	public void doUpdate(IContext context) throws Exception {
	}

	public boolean isShow(int year, int month) {
		Set<String> keySet = statisticsStepWorkTime.keySet();
		for (String key : keySet) {
			StatisticsStepWorkTime workTime = statisticsStepWorkTime.get(key);
			if (workTime != null) {
				int actualFinishYear = workTime.getActualFinishYear();
				int actualFinishMonth = workTime.getActualFinishMonth();
				if (!workTime.isFinishType()
						|| (year == actualFinishYear && month == actualFinishMonth)) {
					return true;
				}
			}
		}

		return false;
	}
}
